/**
 *  This file is part of MunchLevel.
 *
 *  MunchLevel is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  MunchLevel is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with MunchLevel.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.munchlevel.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;

import com.munchlevel.R;

public class CounterView extends ViewGroup implements View.OnClickListener, OnLongClickListener {

	public interface OnViewChangedListener<T extends View> {
		void onChanged(T picker, int oldVal, int newVal);
	}

	private static final int MINIMUM_VALUE = -99;
	private static final int MAXIMUM_VALUE = 999;

	ImageView img;

	Button leftButton;

	Button rightButton;

	private NumberDrawable numberDrawable;

	private OnClickListener onClickListener;

	private OnLongClickListener onLongClickListener;

	private OnViewChangedListener<CounterView> onChangedListener;

	private int value = 0;

	private boolean readOnly = false;

	private Integer overlayColor = null;

	private int minimumValue = MINIMUM_VALUE;
	private int maximumValue = MAXIMUM_VALUE;

	public CounterView(Context context) {
		this(context, null);
	}

	public CounterView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public CounterView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);

		TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CounterView);
		readOnly = a.getBoolean(R.styleable.CounterView_readOnly, false);
		if (!isInEditMode()) {
			LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			inflater.inflate(R.layout.counter, this, true);

			setClickable(true);

			img = (ImageView) findViewById(R.id.counter_iv);
			img.setFocusable(true);

			leftButton = (Button) findViewById(R.id.counter_btn_left);
			if (!readOnly) {
				leftButton.setOnClickListener(this);
				leftButton.setOnLongClickListener(this);
			} else {
				leftButton.setVisibility(View.GONE);
			}
			rightButton = (Button) findViewById(R.id.counter_btn_right);
			if (!readOnly) {
				rightButton.setOnClickListener(this);
				rightButton.setOnLongClickListener(this);
			} else {
				rightButton.setVisibility(View.GONE);
			}

			if (readOnly) {
				img.setOnClickListener(this);
			}

			Drawable drawable = a.getDrawable(R.styleable.CounterView_counterImage);
			if (drawable != null)
				img.setBackgroundDrawable(drawable);

			if (a.hasValue(R.styleable.CounterView_overlayColor))
				overlayColor = a.getColor(R.styleable.CounterView_overlayColor, Color.TRANSPARENT);

			a.recycle();

		}
		// setValue(value);
	}

	public int getMinimumValue() {
		return minimumValue;
	}

	public void setMinimumValue(int minimumValue) {
		this.minimumValue = minimumValue;
	}

	public int getMaximumValue() {
		return maximumValue;
	}

	public void setMaximumValue(int maximumValue) {
		this.maximumValue = maximumValue;
	}

	public void setValue(int value) {
		setValue(value, false);
	}

	public void setValue(int value, boolean showPlus) {
		if (value < minimumValue)
			value = minimumValue;
		else if (value > maximumValue)
			value = maximumValue;

		int oldValue = this.value;

		this.value = value;

		if (numberDrawable == null) {
			numberDrawable = new NumberDrawable(getResources(), value, showPlus);
			if (overlayColor != null && overlayColor != Color.TRANSPARENT) {
				numberDrawable.setColorFilter(new PorterDuffColorFilter(overlayColor, PorterDuff.Mode.SRC_ATOP));
			}
			img.setImageDrawable(numberDrawable);
		}

		numberDrawable.setNumber(value);
		img.invalidate();

		if (oldValue != this.value && onChangedListener != null) {
			onChangedListener.onChanged(this, oldValue, this.value);
		}
	}

	public int getValue() {
		return value;
	}

	@Override
	public void onClick(View v) {

		if (onClickListener != null) {
			onClickListener.onClick(this);
		}
		switch (v.getId()) {
		case R.id.counter_btn_left:
			img.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
					HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
			setValue(getValue() - 1);
			break;
		case R.id.counter_btn_right:
			img.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
					HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
			setValue(getValue() + 1);
			break;
		}
	}

	@Override
	public boolean onLongClick(View v) {
		if (onLongClickListener != null) {
			onLongClickListener.onLongClick(this);
		}

		if (v == leftButton) {
			setValue(getValue() - 5);
			return true;
		} else if (v == rightButton) {
			setValue(getValue() + 5);
			return true;
		}
		return false;
	}

	public OnClickListener getOnClickListener() {
		return onClickListener;
	}

	public void setOnClickListener(OnClickListener onClickListener) {
		this.onClickListener = onClickListener;
	}

	public OnLongClickListener getOnLongClickListener() {
		return onLongClickListener;
	}

	public void setOnLongClickListener(OnLongClickListener onLongClickListener) {
		this.onLongClickListener = onLongClickListener;
	}

	public OnViewChangedListener<CounterView> getOnChangedListener() {
		return onChangedListener;
	}

	public void setOnChangedListener(OnViewChangedListener<CounterView> onChangedListener) {
		this.onChangedListener = onChangedListener;
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right, int bottom) {

		int width = (right - getPaddingRight()) - (left + getPaddingLeft());
		int height = (bottom - getPaddingBottom()) - (top + getPaddingTop());
		MarginLayoutParams layoutParams = null;

		if (img != null) {
			layoutParams = (MarginLayoutParams) img.getLayoutParams();
			img.layout(getPaddingLeft() + layoutParams.leftMargin, getPaddingTop() + layoutParams.topMargin, width
					+ getPaddingLeft() - layoutParams.rightMargin, height + getPaddingTop() - layoutParams.bottomMargin);
		}
		if (leftButton != null) {
			layoutParams = (MarginLayoutParams) leftButton.getLayoutParams();

			leftButton.layout(getPaddingLeft() + layoutParams.leftMargin, getPaddingTop() + layoutParams.topMargin,
					(width / 2) + getPaddingLeft() - layoutParams.rightMargin, height + getPaddingTop()
							- layoutParams.bottomMargin);
		}
		if (rightButton != null) {
			layoutParams = (MarginLayoutParams) rightButton.getLayoutParams();
			rightButton.layout(getPaddingLeft() + (width / 2) + layoutParams.leftMargin, getPaddingTop()
					+ layoutParams.topMargin, width + getPaddingLeft() - layoutParams.rightMargin, height
					+ getPaddingTop() - layoutParams.bottomMargin);
		}
	}

	private int getIntrinsicWidth() {
		if (img != null) {
			return img.getBackground().getIntrinsicWidth() + getPaddingLeft() + getPaddingRight();
		} else {
			return 0;
		}
	}

	private int getIntrinsicHeight() {
		if (img != null) {
			return img.getBackground().getIntrinsicHeight() + getPaddingTop() + getPaddingBottom();
		} else {
			return 0;
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

		int width = measureWidth(widthMeasureSpec);
		int height = measureHeight(heightMeasureSpec);

		if (isAspectRatio()) {
			float intrinsicRatio = (float) getIntrinsicWidth() / getIntrinsicHeight();
			float ratio = (float) width / height;

			if (ratio <= intrinsicRatio) {
				height = (int) (width * intrinsicRatio);
			} else {
				width = (int) (height * intrinsicRatio);
			}

		}
		setMeasuredDimension(width, height);

	}

	@Override
	public LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new MarginLayoutParams(getContext(), attrs);
	}

	@Override
	protected boolean checkLayoutParams(LayoutParams p) {
		return p instanceof MarginLayoutParams;
	}

	private boolean isAspectRatio() {
		if (getLayoutParams() instanceof PercentageLayout.LayoutParams) {
			return ((PercentageLayout.LayoutParams) getLayoutParams()).isAspectRatio();
		} else {
			return false;
		}

	}

	/**
	 * Determines the width of this view
	 * 
	 * @param measureSpec
	 *            A measureSpec packed into an int
	 * @return The width of the view, honoring constraints from measureSpec
	 */
	private int measureWidth(int measureSpec) {
		int result = 0;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);

		if (specMode == MeasureSpec.EXACTLY) {
			// We were told how big to be
			result = specSize;
		} else {
			// Measure the text
			result = (int) getIntrinsicWidth();

			if (specMode == MeasureSpec.AT_MOST) {
				// Respect AT_MOST value if that was what is called for by
				// measureSpec
				result = Math.min(result, specSize);
			}
		}

		return result;
	}

	/**
	 * Determines the height of this view
	 * 
	 * @param measureSpec
	 *            A measureSpec packed into an int
	 * @return The height of the view, honoring constraints from measureSpec
	 */
	private int measureHeight(int measureSpec) {
		int result = 0;
		int specMode = MeasureSpec.getMode(measureSpec);
		int specSize = MeasureSpec.getSize(measureSpec);

		if (specMode == MeasureSpec.EXACTLY) {
			// We were told how big to be
			result = specSize;
		} else {

			// Measure the text (beware: ascent is a negative number)
			result = (int) getIntrinsicHeight();

			if (specMode == MeasureSpec.AT_MOST) {
				// Respect AT_MOST value if that was what is called for by
				// measureSpec
				result = Math.min(result, specSize);
			}
		}
		return result;
	}

}
